Repository navigation
feat(semantics): evaluate KerML Rationals exactly - #838
Conversation
Represent Rational values as normalized exact fractions with a compact small-fraction path and a size budget, parse decimal literals exactly, and carry exact Rationals through runtime, solver replay, gRPC, JSON, the Go client and compiled calculations. Co-Authored-By: jason.han <hanhuijun@gmail.com>
Co-Authored-By: jason.han <hanhuijun@gmail.com>
Co-Authored-By: jason.han <hanhuijun@gmail.com>
Co-Authored-By: jason.han <hanhuijun@gmail.com>
…y-design Co-Authored-By: jason.han <hanhuijun@gmail.com>
Co-Authored-By: jason.han <hanhuijun@gmail.com>
Co-Authored-By: jason.han <hanhuijun@gmail.com>
…ation Co-Authored-By: jason.han <hanhuijun@gmail.com>
…Node Co-Authored-By: jason.han <hanhuijun@gmail.com>
Co-Authored-By: jason.han <hanhuijun@gmail.com>
…n-free Co-Authored-By: jason.han <hanhuijun@gmail.com>
Co-Authored-By: jason.han <hanhuijun@gmail.com>
Co-Authored-By: jason.han <hanhuijun@gmail.com>
|
I'll fix CI failures and address comments from users with write access. I'll skip comments containing "(aside)".
|
…ion and read exact Rational inputs A Rational meeting a binary64 Real is rounded once before comparison, as in mixed arithmetic, so a binding across a Real declaration stays equal. Clients send every exact Rational as rational_value under rational_values, the service reads a binary64-exact one as that Rational, and a realValue stays a Real. Co-Authored-By: jason.han <hanhuijun@gmail.com>
Co-Authored-By: jason.han <hanhuijun@gmail.com> # Conflicts: # client/node/src/core/document.ts
…ned 1.83.0 Co-Authored-By: jason.han <hanhuijun@gmail.com>
…n clients, hold RealFunctions quantity magnitudes as Reals Co-Authored-By: jason.han <hanhuijun@gmail.com>
… meets negative zero Co-Authored-By: jason.han <hanhuijun@gmail.com>
Co-Authored-By: jason.han <hanhuijun@gmail.com>
…s and products Co-Authored-By: jason.han <hanhuijun@gmail.com>
Compiled calcs keep exact Rational folding and refusals through Real-typed values that hold an Integer at run time; a Go program refuses such a run with the compile-time message. The new short-named for-loop fixture accumulates exact Rational literals, as the existing for-loop fixture does. Co-Authored-By: jason.han <hanhuijun@gmail.com>
An Integer to a negative literal power is classified as the exact quotient it is, so exact arithmetic over it is refused rather than rounded, and a Go quotient below the least Real or beyond the greatest fails as the interpreter does. Co-Authored-By: jason.han <hanhuijun@gmail.com>
Co-Authored-By: jason.han <hanhuijun@gmail.com> # Conflicts: # internal/workspace/libs/stdlib.snapshot
Co-Authored-By: jason.han <hanhuijun@gmail.com> # Conflicts: # docs/guide/09-clients.md # docs/reference/python-api.md
…API reference Co-Authored-By: jason.han <hanhuijun@gmail.com>
…ser walkthroughs Co-Authored-By: jason.han <hanhuijun@gmail.com>
Co-Authored-By: jason.han <hanhuijun@gmail.com> # Conflicts: # api/proto/sysml.pb.go # client/java/opensysml-client/src/main/java/org/openmbee/opensysml/proto/Sysml.java # client/julia/OpenSysML/src/OpenSysML.jl # client/node/src/generated/sysml_pb.ts # client/python/opensysml/connection.py # client/python/opensysml/proto/sysml_pb2.py # client/rust/conformance/sysml.descriptor.binpb # client/rust/opensysml/src/operations.rs # docs/reference/service-transports.md # internal/frontend/grpc/convert_big_integer_test.go # internal/frontend/grpc/service.go # internal/workspace/libs/stdlib.snapshot
…accumulation fixture's statements Co-Authored-By: jason.han <hanhuijun@gmail.com>
Co-Authored-By: jason.han <hanhuijun@gmail.com>
Co-Authored-By: jason.han <hanhuijun@gmail.com>
Co-Authored-By: jason.han <hanhuijun@gmail.com> # Conflicts: # client/java/opensysml-client/src/main/java/org/openmbee/opensysml/Capabilities.java # client/java/opensysml-client/src/main/java/org/openmbee/opensysml/proto/Sysml.java # client/julia/OpenSysML/src/OpenSysML.jl # client/julia/OpenSysML/src/capabilities.jl # client/node/src/core/capabilities.ts # client/node/src/generated/sysml_pb.ts # client/opensysml/types.go # client/python/opensysml/__init__.py # client/python/opensysml/capabilities.py # client/python/opensysml/proto/sysml_pb2.py # client/rust/conformance/sysml.descriptor.binpb # client/rust/opensysml/src/capabilities.rs # client/rust/opensysml/src/operations.rs # docs/reference/python-api.md # docs/reference/service-transports.md
… opt-in lint Co-Authored-By: jason.han <hanhuijun@gmail.com>
Co-Authored-By: jason.han <hanhuijun@gmail.com> # Conflicts: # api/proto/sysml.pb.go # client/java/opensysml-client/src/main/java/org/openmbee/opensysml/proto/Sysml.java # client/julia/OpenSysML/test/runtests.jl # client/node/src/generated/sysml_pb.ts # client/python/opensysml/proto/sysml_pb2.py # client/rust/conformance/sysml.descriptor.binpb # client/rust/opensysml/src/operations.rs # docs/reference/service-transports.md # internal/exec/runtime/library_conversions.go # internal/frontend/repl/compile_test.go # internal/workspace/libs/stdlib.snapshot # tests/wasm/engine_test.go
There was a problem hiding this comment.
Devin Review found 1 new potential issue.
⚠️ 1 issue in files not directly in the diff
⚠️ Compiled C strings truncate in diagnostics
When a String contains NUL, sysml_format_str truncates its quoted value at that byte. Diagnostics and formatted collection values omit the remaining characters.
|
Re the Devin Review finding on |
Co-Authored-By: jason.han <hanhuijun@gmail.com> # Conflicts: # internal/workspace/libs/stdlib.snapshot
Co-Authored-By: jason.han <hanhuijun@gmail.com> # Conflicts: # internal/workspace/libs/stdlib.snapshot
Co-Authored-By: jason.han <hanhuijun@gmail.com> # Conflicts: # client/java/opensysml-client/src/main/java/org/openmbee/opensysml/proto/Sysml.java # client/julia/OpenSysML/src/OpenSysML.jl # client/node/src/generated/sysml_pb.ts # client/python/opensysml/__init__.py # client/python/opensysml/proto/sysml_pb2.py # client/rust/conformance/sysml.descriptor.binpb # docs/reference/service-transports.md # internal/workspace/libs/stdlib.snapshot
What and why
A KerML
Rationalis now evaluated as a rational number instead of an IEEE binary64:0.1 + 0.2 == 0.3istrue,1 / 3is the Rational1/3,numer(rat(1, 3))is1.Realstays binary64.docs/project/exact-rational-evaluation.md, which earlier declined this, isrewritten as the record of the new decision; its pilot probes are kept as the documented
divergence from the pilot.
semantics.Valuegains an exactValRational: lowest terms, positive denominator, inline(
int64numerator /uint32denominator) when it fits, an immutablebig.Ratotherwise.Integers stay
ValInt; an Integer beyondint64is now abig.Ratover one.semantics.ParseRational); Integer/is the exactquotient (replaces the once-rounded
IntQuotient);+ - * /,**/^with an Integer exponent,comparisons,
rat,numer,denom,gcd,abs,floor,round,max,min,sum,product,ToString,ToRational,ToIntegerare exact.DefaultMaxIntegerBits(shared with Integers)is
semantics.ErrRationalSizeLimit, never a rounding; powers are refused from a lower boundbefore computing. A harmonic accumulation stops with that typed error.
0.3,2.0,1.5e-05); any other asnumer/denom(1/3). Values that were already binary fractions printunchanged in REPL, JSON and traces.
semantics.RealOf): a value written to aRealfeature (holdAsReal),RealFunctionsoperands, and a Rational meeting a binary64 Real in arithmetic are rounded to thenearest double once.
sqrt, trig,exp,ln, non-Integer**stay binary64.RealFunctions::sum/producthold every magnitude as a Real, an Integer quantity's included(
RealFunctions::sum((1 [m], 2 [m]))is3.0 [m]), as they already did for plain Integers.semantics.CompareReal):== != < <= > >=between a Rational and a binary64 Realcompare the Rational's nearest double with the Real, the same rounding mixed arithmetic does,
so a binding to a
Realfeature holds (x : Real = 0.1; x == 0.1andrat(1, 3) == 1.0 / 3.0are
true). A Rational past the binary64 range compares as ±Inf; NaN is unordered as before.Rational vs Rational stays exact, and so does Integer vs anything (
CompareIntReal). Evaluator,runtime, set membership, query equality, solver replay and
-compileshare the rule. AReal-typed accumulation therefore stays binary64 and never reaches the Rational budget.(
0.1 [m] + 1 [mm]is exactly0.101 [m]).Rationalmessage inValue.rational_value,Quantity.rational_magnitude,DocumentValue.rational_value, negotiated by a newrational_valuescapability exactly asbig_int_values. Answers are canonical: a binary64-exact Rational crosses asreal_value,any other as
rational_value. Inputs: a client sends every exact Rational asrational_valueto a service advertisingrational_values, and the service reads anylowest-terms
rational_value(a binary64-exact one included) as that exact Rational; aninbound
real_valueis always a Real. Toward an older service a client rewrites abinary64-exact Rational as
real_valueand refuses any other, naming the capability; aservice answering a client without the capability refuses (
UNIMPLEMENTED), never a nearestdouble. Non-lowest-terms encodings are rejected (
semantics.LowestTermsRational)."rationalValue": {"numerator": "1", "denominator": "3"}/"rationalMagnitude".opensysml.Rational, Pythonfractions.Fraction(generated classes typeRationalfeaturesFraction), Node{kind: "rational", numerator: bigint, denominator: bigint}, Java/Rust numerator–denominator pairs, JuliaRational{BigInt}, MATLAB decimal-stringstruct; each declares
rational_values. Client value equality (NodevaluesEqual, JavasameValue, Rustsame_value, Juliasame_value) follows the same comparison rule, so aset holding
1/3and1.0 / 3.0is refused as a duplicate.xsd:decimal; exponent literal →xsd:doubleonly when a double holdsit exactly, otherwise exact
xsd:decimal. Round trip is exact.part in is replayed and marked rounded (
Query.Rounded). Census over all fixtures and corpora:6 queries remain marked rounded, all genuinely inexact.
sysml -compile: exact constants are folded; a single operation over binary64-exact values iscompiled with guards; an Integer quotient compared with a whole number is compared exactly.
An Integer to a negative literal power is the exact quotient, rounded once where it reaches a
Real; one below the least Real fails as in the interpreter. A record attribute typed
Realwith a decimal default (
attribute z : Real default 0.1;) holds that default rounded once,as the interpreter does.
rounded-real-literal(off by default): a decimal or exponent literal written to aRealfeature that binary64 cannot hold exactly (attribute x : Real = 0.1;) warns and names thedouble it becomes (
0.10000000000000001);0.25, Integers, computed values andRationalfeatures stay silent. Switch it on with
sysml -enable-lint rounded-real-literal,%lint rounded-real-literal on, the LSP settingenabledLints, ormodel.WithEnabledLints;disabling wins over enabling. The gRPC service reports only on-by-default lints.
Still refused, and why:
sysml -compilerefuses — with a typed error naming the construct —a
Rationalparameter,a * 0.1over a variable,(a * 0.5) ** 2,a / 3 < 0.1,a ** -1 * 3, and collectionoperations over exact Rationals, because compiled code holds numbers as
int64/binary64 and thesewould need more than one rounding. A Go
Realparameter given an Integer meets an exact Rational only at run time, so there theprogram fails with the same message. Arithmetic over an infinity remains a typed error, as for
LiteralInfinitytoday.Two policy choices the specification does not settle, recorded as tool-defined in the
evaluation record:
above. Exact comparison would make
x : Real = 0.1; x == 0.1false and break the equality abinding asserts (KerML §7.4.9), since
holdAsRealrounds the bound value.rational_valueunderrational_values; an inboundreal_valueis always a Real; runtime semantics do not depend on the wire form.Specification basis
positive and negative infinity"; §9.3.2.2.9: Real "includes both rational and irrational numbers".
the KerML DataType Rational" — a finite decimal names a rational exactly.
RationalFunctions.kerml(§9.4.10) andIntegerFunctions.kerml(§9.4.11), vendored underinternal/workspace/libs: the declared result types decide classification — Integer/returnsRational, so6 / 3is the Rational2(istype Integerisfalse);floor,round,numer,denom,gcd, Integer+ - * % **returnInteger.RationalFunctions::'**'declaresy: Rational, but a non-Integer exponent has in general anirrational result, so only Integer exponents are exact; others are
RealFunctions::'**'.binary64 Real is at Real precision.
Realprecision, how a Rationalmeets a binary64 Real, the wire form of an input, print form. UML/fUML/PSSM say nothing about KerML Rationals.
docs/project/spec-compliance.md: new/updated rows for literals, arithmetic, representation,printing, the Real boundary, every wire boundary, RDF,
-compile,rat/numer/denom,floor/round, Integer/, solver replay and rounded marking.Pilot differs by design. The pinned pilot holds
LiteralRationalas a Javadouble. The tenprobes are committed as
tools/referee/exec/testdata/cases/exact_rationals.casesunder a newby-design: <clause>marker; five land in a newdiffers-by-designbucket with the clause besideboth raw outputs (
0.1 + 0.2 == 0.3,0.1 + 0.2 <= 0.3,0.3 < 0.1 + 0.2,(1.0 / 49.0) * 49.0 == 1.0, ten-fold0.1sum== 1.0), five agree within the harness'stwo-decimal tolerance. Referee run against
develop(446 cases) and this branch (456):agree 212→217 · differs-by-design 0→5, every other bucket unchanged (disagree 29,pilot-error 5,both-error 16, …); a per-case comparison of the two reports shows none of the446 existing cases moved bucket. The stale totals in
pilot-execution-referee.mdand thereferee skill are updated to the measured ones.
How it was verified
Four-layer tests: golden AST fixture; conformance
action_exact_rational_accumulation.sysml+.expected.json; golden traceaction_exact_rational_accumulation.trace.golden;robustness_exact_rational_test.go(
TestRuntimeRobustnessExactRational: denominator growth beyond a lowered budget, a power and aliteral beyond the size budget, division by zero — each a typed error);
robustness_rational_real_comparison_test.go(TestRuntimeRobustnessRationalRealComparison:Rational vs NaN, vs ±Inf, a Rational beyond the binary64 range vs a large Real, set membership,
and a 1000-step
Realaccumulation of0.01under a lowered budget). Conformanceinstance_real_binding_meets_rational(binding equality,rat(1,3) == 1.0/3.0,rat(1,4) == 0.25)and
action_real_accumulation_stays_binary64. gRPC conformance covers both wire directions(
evaluate_calc_rational_wire*,execute_action_rational_input_meets_rational,execute_action_real_input_meets_rational), as do the Python, Node, Julia, Rust, Java and MATLABclient tests.
-compilefixtures cover each comparison operator against the nearest double andrefuse a literal beyond the Real range.
Semantics unit tests cover storage, parsing, formatting, arithmetic, and wire validation.
training_examples_expected.txtis untouched; no corpus or RDF ratchet moved. Locally, GNU Octave6.4 lacks
jsonencode/jsondecode, so only the MATLAB encoding and capability tests ran there;the MATLAB/Octave CI job runs the full suite.
The browser REPL walkthroughs (
docs/assets/repl-walkthroughs.json, checked byTestBrowserREPLWalkthroughs) now expect100 [SI::km] / 2 [SI::h]as the exact125/9 [SI::'m/s'],%calc Speed 42.195 [SI::km] 2 [SI::h]as2813/480 [SI::'m/s'], and theRealFunctions::sumrollup
car.massas1300.0 [kg].Benchmarks (develop vs this branch, interleaved, six samples each,
benchstat):internal/exec/runtime, seven existingIntegerArithmeticLoopBeyondInt64tests/perf(REPLEvalExpr, ExecuteAction, BatchConstraints, SameConstraintManyInstances, BatchSatisfy, Instantiate, GRPCEvaluate)RationalDecimalLoop/RealDecimalLoopRationalHarmonicLoop(200 exact terms of 1/i)The byte increase beyond
int64is thebig.Rat-over-one representation; the harmonic loop is thegenuine cost of exactness (denominator of hundreds of bits), bounded by the size budget.
Checklist
make testandmake lintpass locallychanges/unreleased/<slug>.<section>.md, not as an edit toCHANGELOG.mdmake docs-countsrun if a gate count moved (compliance rows need nothing: the census is counted at docs build)F4,K5) in the body, docs, or changelogLink to Devin session: https://nasa-jpl-demo.devinenterprise.com/sessions/0fe9b9a0b2db4a9a8d398dd29dd3ec33
Open in Devin Desktop: https://nasa-jpl-demo.devinenterprise.com/desktop/session/0fe9b9a0b2db4a9a8d398dd29dd3ec33?variant=devin
Requested by: @HuiJun